/*
 * 说明:调度服务器，用于对单种类型的服务器集群进行调度
 *         服务器通过接口向调度服务器注册自身地址
 *         客户端通过接口向调度服务器申请一个可用的服务器地址
 *         服务器需要向调度服务器发心跳，调度服务器识别到服务器掉线后将其剔除
 * 作者:陶涛
 * */
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "updDispatch.h"

#define _FL_    __FILE__,__func__,__LINE__
#define updError(fmt, args...) \
        printf("%s:%s:%u--->"fmt, _FL_, ##args);\
        printf("\n")

#include "updTcpService.c"

#include <stdio.h>
#include <unistd.h>

void updDaemon(void)
{
    char path[256] = {};

    getcwd(path, sizeof(path));
    daemon(0, 0);
    chdir(path);

    return;
}

int updWritePidfile(const char *pidfile)
{
    int   fd  = -1;
    pid_t pid = 0;
    char  buf[16] = {};

    fd = open(pidfile, O_CREAT|O_RDWR, 0600);
    if(-1 == fd){
        return -1;
    }

    if(-1 == flock(fd, LOCK_EX|LOCK_NB)){
        close(fd);
        return -1;
    }

    pid = getpid();
    snprintf(buf, sizeof(buf), "%-15d", pid);
    lseek(fd, 0, SEEK_SET);
    write(fd, buf, strlen(buf));
    flock(fd, LOCK_UN);
    close(fd);
    return 0;
}

int updCheckPidfile(const char *pidfile)
{
    int   fd  = -1;
    int   cnt = -1;
    pid_t pid = 0;
    char  buf[16] = {};

    fd = open(pidfile, O_CREAT|O_RDWR, 0600);
    if(-1 == fd){
        printf("\n\t提示:创建启动锁文件[%s]错误!!!\n\t\t请确定目录的权限\n\n", pidfile);
        return -1;
    }

    if(-1 == flock(fd, LOCK_EX|LOCK_NB)){
        printf("\n\t提示:多个程序同时启动,对文件[%s]加锁失败!!!\n\n", pidfile);
        close(fd);
        return -1;
    }

    cnt = read(fd, buf, sizeof(buf));
    buf[cnt] = 0;
    pid = atoi(buf);
    if(pid > 1){
        if(0 == kill(pid, 0)){
            printf("\n\t提示:程序已启动,其pid为[%d]!!!\n\n", pid);
            flock(fd, LOCK_UN);
            close(fd);
            return 1;
        }
    }

    flock(fd, LOCK_UN);
    close(fd);
    return 0;
}


int updDispatcherInitialize(int argc, char **argv, updDispatcher *server)
{
    int i = 1;
    char isdaemon[5]="no";
    char pidfile[256] = {};
    struct rlimit limit;

    if(1 == argc){
        printf("\n\t必须在命令行定义要调度的服务:");
        printf("--service=服务名\n\n");
        exit(1);
    }

    server->lport = 5001;
    server->maxfd = 1000;
    server->tcpConnTimeout = 60;
    server->srvHeartTimeout = 6;
    server->service[0] = 0;
    snprintf(pidfile, sizeof(pidfile), "%s.pid", basename(argv[0]));
    
    while(i < argc){
        if(0 == strncmp("--lport=", argv[i], 8)){
            server->lport = atoi(argv[i] + 8);
        }
        else if(0 == strncmp("--pidfile=", argv[i], 10)){
            strncpy(pidfile, argv[i] + 10, sizeof(pidfile) - 1);
        }
        else if(0 == strncmp("--daemon=", argv[i], 9)){
            strncpy(isdaemon, argv[i] + 9, sizeof(isdaemon) - 1);
        }
        else if(0 == strncmp("--maxfd=", argv[i], 8)){
            if(atoi(argv[i] + 8) > server->maxfd)
                server->maxfd = atoi(argv[i] + 8);
        }
        else if(0 == strncmp("--tcpConnTimeout=", argv[i], 17)){
            server->tcpConnTimeout = atoi(argv[i] + 17);
        }
        else if(0 == strncmp("--srvHeartTimeout=", argv[i], 18)){
            server->srvHeartTimeout = atoi(argv[i] + 18);
        }
        else if(0 == strncmp("--service=", argv[i], 10)){
            if(strlen(argv[i] + 10) >= sizeof(server->service)){
                updError("%s 超长", argv[i]);
                return -1;
            }
            strcpy(server->service, argv[i] + 10);
        }
        i++;
    }

    if(!server->service[0]){
        updError("未定义要调度的服务:--service=服务名.必须在命令行定义");
        return -1;
    }
    printf("******************************************\n");
    printf("%s args:\n", basename(argv[0]));
    printf("\tdaemon         : --daemon=%s #yes or no\n", isdaemon);
    printf("\tpidfile        : --pidfile=%s\n", pidfile);
    printf("\tservice        : --service=%s\n",server->service);
    printf("\tlisten port    : --lport=%d\n", server->lport);
    printf("\tmaxfd          : --maxfd=%d\n", server->maxfd);
    printf("\ttcpConnTimeout : --tcpConnTimeout=%d #客户端请求,短连接有效\n", server->tcpConnTimeout);
    printf("\tsrvHeartTimeout: --srvHeartTimeout=%d #服务器登记,长连接心跳有效\n", server->srvHeartTimeout);
    printf("******************************************\n");
    printf("\n");

    if(0 != updCheckPidfile(pidfile)) exit(1);
    if(0 == strcmp(isdaemon, "yes")) updDaemon();
    if(0 != updWritePidfile(pidfile)){
        updError("updWritePidfile [%s] error", pidfile);
        return -1;
    }

    if(-1 == getrlimit(RLIMIT_NOFILE, &limit)){
        updError("getrlimit error:%s", strerror(errno));
        return -1;
    }
    if(server->maxfd > limit.rlim_cur){
        if(0 == getuid()){
            limit.rlim_cur = server->maxfd;
            limit.rlim_max = server->maxfd;

            if(-1 == setrlimit(RLIMIT_NOFILE, &limit)){
                updError("setrlimit error:%s", strerror(errno));
                return -1;
            }
        }
        else{
            updError("maxfd = %d > ulimit -n = %lu\n", server->maxfd, limit.rlim_cur);
            return -1;
        }
    }

    server->srvList = ralCreate();
    if(!server->srvList){
        updError("ralCreate error:%s", strerror(errno));
        return -1;
    }

    server->loopIterator = ralCreateIterator(server->srvList);
    if(!server->loopIterator){
        updError("ralCreateIterator error:%s", strerror(errno));
        return -1;
    }

    server->tcpclients = calloc(1, server->maxfd * sizeof(updTcpClient));
    if(!server->tcpclients){
        updError("calloc error:%s", strerror(errno));
        return -1;
    }

    server->loop = aeCreateEventLoop(server->maxfd);
    if(!server->loop){
        updError("aeCreateEventLoop error:%s", strerror(errno));
        return -1;
    }

    server->tlfd = updTcpCreateListenFd(server->lport);
    if(-1 == server->tlfd){
        updError("aeCreateEventLoop error:%s", strerror(errno));
        return -1;
    }

    return 0;
}



int main(int argc, char **argv)
{
    int ret;
    updDispatcher server;

    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    ret = updDispatcherInitialize(argc, argv, &server);
    if(-1 == ret){
        updError("updDispatcherInitialize error");
        return -1;
    }
    
    ret = aeCreateFileEvent(server.loop, server.tlfd, AE_READABLE, updTcpAccept, &server);
    if(-1 == ret){
        updError("aeCreateFileEvent error");
        return -1;
    }

    ret = aeCreateTimeEvent(server.loop, server.srvHeartTimeout * 300, updSrvHeartTimeout, &server, NULL);
    if(-1 == ret){
        updError("aeCreateTimeEvent error");
        return -1;
    }

    ret = aeCreateTimeEvent(server.loop, server.tcpConnTimeout * 200, updTcpTimeout, &server, NULL);
    if(-1 == ret){
        updError("aeCreateTimeEvent error");
        return -1;
    }

    aeMain(server.loop);

    return 0;
}


